#
# Simple xml / html parser
# only works on a bare stripped html version of the functions index

# Quicly Whipped together by Dirk van Oosterbosch, oktober 2008
# See the project at google code
# http://code.google.com/p/xcode-tokens-from-qtdocs
# for more information about it
# and help how to use it

functionsFile = "functions.html"
allClassesFile = "classes.html"

import re
import elementtree.ElementTree as ET
tree = ET.parse(functionsFile)

# These are doc files which contain global functions, macro's etc.
# In function.html they appear as "global", so we need to make an exception for these.
GLOBAL_EXCEPTIONS = ["qtalgorithms.html", "qtglobal.html", "qtplugin.html"]

classes = {}
counter = 0
funcNamePattern = re.compile(r"[a-zA-Z0-9_]+")
classLinkPattern = re.compile(r"([a-zA-Z0-9_-]+)(\.\w+)(#)([a-zA-Z0-9_]+)")
clsLnkPattern = re.compile(r"([a-zA-Z0-9_-]+)(\.\w+)")

for functionItem in tree.findall("//li"):
	funcName = funcNamePattern.match(functionItem.text)
	if funcName:
		counter += 1
		for classlink in functionItem.findall("a"):
			if classlink.get("href"):
				mainClassLink = classLinkPattern.match(classlink.get("href"))
				if mainClassLink:
					theClassName = classlink.text
					theClassFile = mainClassLink.group(1)
					if not classes.has_key(theClassFile):
						# Not yet in our class dictionary: so add it
						classDocument = mainClassLink.group(1) + mainClassLink.group(2)
						classes[theClassFile] = {'name': theClassName, 'link': classDocument, 'functions': [] }
					# Check for 'global'
					if (classes[theClassFile]['name'] == 'global' and not theClassName == 'global' ):
						# Set the right class name:
						classes[theClassFile]['name'] = theClassName
					theFunctionName = funcName.group()
					theFunctionAnchor = mainClassLink.group(4)
					globalFlag = (theClassName == 'global')
					# check if we don't create duplicates
					if not (theFunctionName in [f['name'] for f in classes[theClassFile]['functions']]):
						classes[theClassFile]['functions'].append({'name': theFunctionName, 'anchor': theFunctionAnchor, 'global': globalFlag})
				else:
					print "ERROR no MainClassLink was found in " + classlink.get("href")
			else:
				# A common element which we don't parse is the alphabetical name anchor
				# (the anchors to jump to 'A', 'B', etc)
				if classlink.get("name"):
					# print "WARNING, name anchor found: %s" % (classlink.get("name"))
					pass
				else:
					print "ERROR no href or name link was found in "
					print ET.dump(classlink)
	else:
		print "ERROR no functionname was found in "
		print ET.dump(functionItem)
print "%d functions found in %s" % (counter, functionsFile)
print "(In Qt 4.4.1, there should be 6605 functions)" # In the documentation of QT 4.4.1 there should be 6605 function
print "%d classes found in %s" % (len(classes), functionsFile)


# Parse the All Classes index
tree = ET.parse(allClassesFile)
print "Parsing %s for classes not listed in %s ..." % (allClassesFile, functionsFile)
extraCounter = 0
for classlink in tree.findall("//a"):
	if classlink.get("href"):
		clslnk = clsLnkPattern.match(classlink.get("href"))
		if clslnk:
			# This is valid class link.
			# Lets check if we don't already include this one in the functions list
			theClassFile = clslnk.group(1)
			theClassName = classlink.text
			if not classes.has_key(theClassFile):
				# Not yet in our class dictionary: so add it
				extraCounter += 1
				classDocument = mainClassLink.group(1) + mainClassLink.group(2)
				classes[theClassFile] = {'name': theClassName, 'link': classDocument, 'functions': [] }
		else:
			print "ERROR: no classlink in %s" % (classlink.get("href"))
print "%d extra classes found in %s" % (extraCounter, allClassesFile)


# Make the tokens file
tokensFile = """<?xml version="1.0" encoding="UTF-8"?>
<Tokens version="1.0"> <!-- Root element -->
"""
for k in classes:
	aClass = classes[k]
	if (aClass['name'] == 'global'):
		if (aClass['link'] in GLOBAL_EXCEPTIONS):
			tokensFile += '\t<File path="%s">\n' % (aClass['link'])
			for func in aClass['functions']:
				tokensFile += "\t\t<Token>\n"
				tokensFile += "\t\t\t<TokenIdentifier>//apple_ref/func/%s</TokenIdentifier>\n" % (func['name'])
				tokensFile += "\t\t\t<Anchor>%s</Anchor>\n" % (func['anchor'])
				tokensFile += "\t\t</Token>\n"
			tokensFile += '\t</File>\n'
		else:
			# A global exception, which I don't know what to do with
			print "Not processing "
			print aClass
	else:
		tokensFile += '\t<File path="%s">\n' % (aClass['link'])
		tokensFile += "\t\t<Token>\n"
		tokensFile += "\t\t\t<TokenIdentifier>//apple_ref/cpp/cl/%s</TokenIdentifier>\n" % (aClass['name'])
		tokensFile += "\t\t</Token>\n"
		for func in aClass['functions']:
			tokensFile += "\t\t<Token>\n"
			if (func['global']):
				# If it is global, don't use the class in the marker string, and make it func
				tokensFile += "\t\t\t<TokenIdentifier>//apple_ref/cpp/func/%s</TokenIdentifier>\n" % (func['name'])
			else:
				# If it is a function of a class, consider it a instm
				tokensFile += "\t\t\t<TokenIdentifier>//apple_ref/cpp/instm/%s/%s</TokenIdentifier>\n" % (aClass['name'], func['name'])
			tokensFile += "\t\t\t<Anchor>%s</Anchor>\n" % (func['anchor'])
			tokensFile += "\t\t</Token>\n"
		tokensFile += '\t</File>\n'
tokensFile += "</Tokens>\n"

fsock = open("Tokens.xml", "wb")
fsock.write (tokensFile)
fsock.close()
print "Tokens.xml file written"